package naga.x.service;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import naga.x.game.time.TimeUpdater;
import naga.x.util.ServiceUtils;

public class DefaultServiceManager implements ServiceManager {
	
	protected static final Logger LOG = LoggerFactory.getLogger(DefaultServiceManager.class);
	
	protected Map<Class<? extends Service>, Service> services;
	
	public DefaultServiceManager() {
		this(64);
	}
	
	public DefaultServiceManager(int initialCapacity) {
		services = new LinkedHashMap<Class<? extends Service>, Service>(initialCapacity);
	}
	
	@Override
	public <T extends Service> T create(String serviceName) {
		try {
			Class<T> clazz = (Class<T>) Thread.currentThread().getContextClassLoader().loadClass(serviceName);
			return create(clazz);
		} catch (Exception e) {
			LOG.error("[SERVICE CREATE]ERROR", e);
		}
		return null;
	}

	@Override
	public <T extends Service> T create(Class<T> serviceClass) {
		try {
			T service = ServiceUtils.newService(serviceClass);//serviceClass.newInstance();
			long t = TimeUpdater.getInstance().utc();
			service.startup();
			LOG.info("[SERVICE]STARTUP OK[" + service.getId() + "]" + (TimeUpdater.getInstance().utc() - t));
			add(serviceClass, service);
			return service;
		} catch (Exception e) {
			LOG.error("[SERVICE CREATE]ERROR", e);
		}
		return null;
	}
	
	@Override
	public <T extends Service> void add(T service) {
		add0(service.getClass(), service);
	}
	
	@Override
	public <K extends Service, T extends K> void add(Class<K> serviceKey, T service) {
		add0(serviceKey, service);
	}
	
	private void add0(Class<? extends Service> key, Service value) {
		services.put(key, value);
	}
	
	@Override
	public <T extends Service> void remove(T service) {
		Class<T> key = (Class<T>) service.getClass();
		if (services.containsKey(key)) {
			remove(key);
			return;
		}
		Iterator<Entry<Class<? extends Service>, Service>> itr = services.entrySet().iterator();
		while (itr.hasNext()) {
			Entry<Class<? extends Service>, Service> e = itr.next();
			if (e.getValue() == service) {
				remove(e.getKey());
				return;
			}
		}
		LOG.info("[SERVICE REMOVE]NOT FOUND[" + key.getName() + "]");
	}
	
	@Override
	public <T extends Service> void remove(Class<T> serviceKey) {
		T service = (T) services.remove(serviceKey);
		if (service == null) {
			LOG.info("[SERVICE REMOVE]NOT FOUND[" + serviceKey.getName() + "]");
			return;
		}
		try {
			service.shutdown();
			LOG.info("[SERVICE REMOVE]SUCC[" + service.getId() + "]");
		} catch (ServiceException e) {
			LOG.info("[SERVICE REMOVE]FAIL[" + service.getId() + "]");
		}
	}
	
	@Override
	public <T extends Service> T get(Class<T> serviceKey) {
		return (T) services.get(serviceKey);
	}
	
	@Override
	public void shutdown() {
		Collection<Service> vals = services.values();
		Service[] valsArr = vals.toArray(new Service[vals.size()]);
		int len = valsArr.length;
		int succ = 0, fail = 0;
		LOG.info("[SERVICE SHUTDOWN]TRY[" + len + "]");
		for (int i = len - 1; i >= 0; i--) {
			Service s = valsArr[i];
			try {
				s.shutdown();
				succ++;
				LOG.info("[SERVICE SHUTDOWN]SUCC[" + s.getId() + "]");
			} catch (Throwable e) {
				fail++;
				LOG.error("[SERVICE SHUTDOWN]FAIL[" + (s != null ? s.getId() : null) + "]", e);
			}
		}
		LOG.info("[SERVICE SHUTDOWN]SUCC[" + succ + "]FAIL[" + fail + "]");
	}

}
